/**
 * Copyright (C), 2018 store
 */
package com.swak.frame.api.filter.spi;

import java.util.Collection;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.frame.chain.FilterInvoker;
import com.swak.frame.common.SpringBeanFactory;
import com.swak.frame.context.BoundContext;
import com.swak.frame.dto.base.SysRestCode;
import com.swak.frame.filter.ApiFilter;
import com.swak.frame.ratelimit.adapter.LimitRaterAdapter;
import com.swak.frame.ratelimit.annotation.RateLimit;
import com.swak.frame.ratelimit.config.LimitOperation;
import com.swak.frame.ratelimit.exception.RateLimitException;
import com.swak.frame.ratelimit.interceptor.RaterOperationSource;

/**
 * 接口限流 ClassName: RateLimitFilter.java
 * 
 * @author colley.ma
 * @date 2021年3月30日 下午2:54:05
 */
public class RateLimitFilter implements ApiFilter {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private LimitRaterAdapter limitRaterAdapter = SpringBeanFactory.getBean(LimitRaterAdapter.class);

	private RaterOperationSource raterOperationSource = SpringBeanFactory
			.getBean(RaterOperationSource.class);

	/**
     * QPS必须大于0，才能确定是否设置限流，enable的设置可以根据配置来
     */
	@Override
	public void doFilter(BoundContext context, FilterInvoker<BoundContext> nextFilter) {
		logger.info("start doFilter:{}", DateTime.now().toString());
		RateLimit rateLimit = context.getRateLimit();
		if(ObjectUtils.anyNull(rateLimit,limitRaterAdapter,raterOperationSource)) {
			//下一个流程
			nextFilter.invoke(context);
			return;
		}
		
		if(rateLimit.qps()>0) {
			if(!isAcquire(context)){
				String retCode = StringUtils.firstNonEmpty(rateLimit.retCode(),SysRestCode.SYS_REQ_LIMIT.getRetCode());
				throw new RateLimitException(retCode, SysRestCode.SYS_REQ_LIMIT.getRetMessage());
			}
		}
		//下一个流程
		nextFilter.invoke(context);
	}

	/**
	 * 限流判断
	 * @param context
	 * @return
	 */
	private boolean isAcquire(BoundContext context) {
		if (raterOperationSource != null) {
			Collection<LimitOperation> operations = raterOperationSource.getBasicOperations(context.getMethod(),
					context.getTargetClass());
			if (!CollectionUtils.isEmpty(operations)) {
				LimitOperation limitOperation = operations.iterator().next();
				return limitRaterAdapter.tryAcquire(limitOperation);
			}
		}
		return true;
	}
}
